aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/groups/[groupId]/@modal
diff options
context:
space:
mode:
authorSebastien Castiel <sebastien@castiel.me>2023-12-19 09:36:40 -0500
committerGitHub <noreply@github.com>2023-12-19 09:36:40 -0500
commit1e66efe5169dfad9a0c62ba42fbf31ce977bf49e (patch)
tree3c2ea81a8b2dd9ba989855de553d65b1bb90b7ad /src/app/groups/[groupId]/@modal
parent66ab0ff82bbcb3294d3a0fb84ae5ffcae9910e26 (diff)
Use modal dialogs for expense creation & edition (#10)
* First attemps at using route interception and modals * Remove route interception * Make it work * Use Vaul on small screens * Improve vaul
Diffstat (limited to 'src/app/groups/[groupId]/@modal')
-rw-r--r--src/app/groups/[groupId]/@modal/default.tsx3
-rw-r--r--src/app/groups/[groupId]/@modal/expense-modal.tsx83
-rw-r--r--src/app/groups/[groupId]/@modal/expenses/[expenseId]/edit/page.tsx26
-rw-r--r--src/app/groups/[groupId]/@modal/expenses/create/page.tsx24
4 files changed, 136 insertions, 0 deletions
diff --git a/src/app/groups/[groupId]/@modal/default.tsx b/src/app/groups/[groupId]/@modal/default.tsx
new file mode 100644
index 0000000..86b9e9a
--- /dev/null
+++ b/src/app/groups/[groupId]/@modal/default.tsx
@@ -0,0 +1,3 @@
+export default function Default() {
+ return null
+}
diff --git a/src/app/groups/[groupId]/@modal/expense-modal.tsx b/src/app/groups/[groupId]/@modal/expense-modal.tsx
new file mode 100644
index 0000000..17bb302
--- /dev/null
+++ b/src/app/groups/[groupId]/@modal/expense-modal.tsx
@@ -0,0 +1,83 @@
+'use client'
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog'
+import { useRouter } from 'next/navigation'
+import { ReactNode, useEffect, useState } from 'react'
+import { Drawer } from 'vaul'
+
+type Props = {
+ children: ReactNode
+ title: ReactNode
+}
+
+export function ExpenseModal(props: Props) {
+ const size = useTailwindBreakpoint()
+ if (size === 'xs') {
+ return <ExpenseVaul {...props} />
+ } else {
+ return <ExpenseDialog {...props} />
+ }
+}
+
+export function ExpenseDialog({ children, title }: Props) {
+ const router = useRouter()
+
+ return (
+ <Dialog open onOpenChange={() => router.back()}>
+ <DialogContent className="w-full max-w-screen-sm">
+ <DialogHeader>
+ <DialogTitle>{title}</DialogTitle>
+ </DialogHeader>
+ {children}
+ </DialogContent>
+ </Dialog>
+ )
+}
+
+export function ExpenseVaul({ children, title }: Props) {
+ const router = useRouter()
+ return (
+ <Drawer.Root open onClose={() => router.back()}>
+ <Drawer.Portal>
+ <Drawer.Title>{title}</Drawer.Title>
+ <Drawer.Overlay className="fixed inset-0 bg-background/80 backdrop-blur-sm" />
+ <Drawer.Content className="bg-background border flex flex-col rounded-t-[10px] max-h-[90dvh] mt-24 fixed bottom-0 left-0 right-0 z-50">
+ <div className="mx-auto w-12 h-1.5 flex-shrink-0 rounded-full bg-gray-300 dark:bg-gray-700 mt-4"></div>
+ <div className="text-xl font-bold p-4">{title}</div>
+ <div className="flex-1 overflow-y-auto p-4 pt-0">{children}</div>
+ </Drawer.Content>
+ </Drawer.Portal>
+ </Drawer.Root>
+ )
+}
+
+export function useTailwindBreakpoint() {
+ const [size, setSize] = useState<'xs' | 'sm' | 'md' | 'lg'>('xs')
+
+ useEffect(() => {
+ const handleBreakpointChange = () => {
+ if (window.innerWidth >= 1200) {
+ setSize('lg')
+ } else if (window.innerWidth >= 768) {
+ setSize('md')
+ } else if (window.innerWidth >= 640) {
+ setSize('sm')
+ } else {
+ setSize('xs')
+ }
+ }
+
+ window.addEventListener('resize', handleBreakpointChange)
+ handleBreakpointChange()
+
+ return () => {
+ window.removeEventListener('resize', handleBreakpointChange)
+ }
+ }, [])
+
+ return size
+}
diff --git a/src/app/groups/[groupId]/@modal/expenses/[expenseId]/edit/page.tsx b/src/app/groups/[groupId]/@modal/expenses/[expenseId]/edit/page.tsx
new file mode 100644
index 0000000..fb70f45
--- /dev/null
+++ b/src/app/groups/[groupId]/@modal/expenses/[expenseId]/edit/page.tsx
@@ -0,0 +1,26 @@
+import { ExpenseModal } from '@/app/groups/[groupId]/@modal/expense-modal'
+import { ExpenseForm } from '@/components/expense-form'
+import { getExpense, getGroup } from '@/lib/api'
+import { Metadata } from 'next'
+import { notFound } from 'next/navigation'
+
+export const metadata: Metadata = {
+ title: 'Edit expense',
+}
+
+export default async function EditExpensePage({
+ params: { groupId, expenseId },
+}: {
+ params: { groupId: string; expenseId: string }
+}) {
+ const group = await getGroup(groupId)
+ if (!group) notFound()
+ const expense = await getExpense(groupId, expenseId)
+ if (!expense) notFound()
+
+ return (
+ <ExpenseModal title="Edit expense">
+ <ExpenseForm group={group} expense={expense} />
+ </ExpenseModal>
+ )
+}
diff --git a/src/app/groups/[groupId]/@modal/expenses/create/page.tsx b/src/app/groups/[groupId]/@modal/expenses/create/page.tsx
new file mode 100644
index 0000000..93cc413
--- /dev/null
+++ b/src/app/groups/[groupId]/@modal/expenses/create/page.tsx
@@ -0,0 +1,24 @@
+import { ExpenseModal } from '@/app/groups/[groupId]/@modal/expense-modal'
+import { ExpenseForm } from '@/components/expense-form'
+import { getGroup } from '@/lib/api'
+import { Metadata } from 'next'
+import { notFound } from 'next/navigation'
+
+export const metadata: Metadata = {
+ title: 'Create expense',
+}
+
+export default async function ExpensePage({
+ params: { groupId },
+}: {
+ params: { groupId: string }
+}) {
+ const group = await getGroup(groupId)
+ if (!group) notFound()
+
+ return (
+ <ExpenseModal title="Create expense">
+ <ExpenseForm group={group} />
+ </ExpenseModal>
+ )
+}